<?php

/**
 * JingYao-backend
 *
 * @link     https://gitee.com/wang-zhihui-release/jingyao-backend
 * @apiDocument https://gitee.com/wang-zhihui-release/jingyao-backend/wikis/
 */

namespace App\Services;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;

class ZhanXinPublicArticleService
{
    const GET_ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token';

    const GET_MATERIAL_URL = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=%s';

//    const GET_MATERIAL_URL = 'https://api.weixin.qq.com/cgi-bin/freepublish/batchget?access_token=%s';

    const GET_MATERIAL_COUNT_URL = 'https://api.weixin.qq.com/cgi-bin/material/get_materialcount';

    const SAVE_LOCAL_PIC_PATH = 'static_files/wximages/zhanxin/';

    const ACCESS_TOKEN_CACHE_KEY = 'wx_access_token';

    const SKIP_ZHANXIN_ARTICLE_NUM_KEY = 'wx_skip_article_number';

    protected $httpService;

    private $appId;

    private $secret;

    public function __construct(HttpService $httpService)
    {
        $this->httpService = $httpService;
        $this->appId = config('zhanxin.app_id');
        $this->secret = config('zhanxin.secret');
    }

    public function getZhanxinArticlesList(int $page, int $pageSize)
    {
        $articles = $this->fetchArticles($page, $pageSize);
        if (isset($articles['errcode']) && $articles['errcode'] == 40001) {
            $this->flushCache();
            $articles = $this->fetchArticles($page, $pageSize);
        }
        return $this->formatWxArticles($articles, $page, $pageSize);
    }

    public function setSkipNum(int $skipNum)
    {
        Cache::flush();
        Redis::connection()->set(self::SKIP_ZHANXIN_ARTICLE_NUM_KEY, $skipNum);
        return true;
    }

    public function getSkipNum()
    {
        return Redis::connection()->get(self::SKIP_ZHANXIN_ARTICLE_NUM_KEY) ?? 1;
    }

    protected function fetchArticles(int $page, int $pageSize)
    {
        $cache = Cache::store('file');
        $key = sprintf('wx_article_list_%s_%s', $page, $pageSize);
        if ($cache->has($key)) {
            return $cache->get($key);
        }
        $skipNum = Redis::connection()->get(self::SKIP_ZHANXIN_ARTICLE_NUM_KEY);
        if (is_null($skipNum)) {
            $skipNum = 1;
        }
        // 微信每波(四章)算一条
        $putWxPageSize = ceil($pageSize / 4);
        $articles = $this->httpService->post(sprintf(self::GET_MATERIAL_URL, $this->fetchAccessToken()), [
            'type' => 'news',
            'offset' => ($page - 1) * $putWxPageSize + (int)$skipNum,
            'count' => $putWxPageSize,
            //            'no_content' => 1,
        ]);
        $articlesRes = json_decode($articles, true);
        $cache->put($key, $articlesRes, 3600);
        return $articlesRes;
    }

    protected function fetchArticlesCount()
    {
        return 1000; // TODO 总数目前来说用不到, 还会有调用次数限制, 直接返回个常数就行
        $countRes = $this->httpService->get(self::GET_MATERIAL_COUNT_URL, [
            'access_token' => $this->fetchAccessToken(),
        ]);
        $count = json_decode($countRes, true)['news_count'];
        return $count * 4;
    }

    protected function fetchAccessToken()
    {
        if (Redis::connection()->get(self::ACCESS_TOKEN_CACHE_KEY)) {
            return Redis::connection()->get(self::ACCESS_TOKEN_CACHE_KEY);
        }
        $accessTokenRes = $this->httpService->get(self::GET_ACCESS_TOKEN_URL, [
            'grant_type' => 'client_credential',
            'appid' => $this->appId,
            'secret' => $this->secret,
        ]);
        /** @var string $accessToken */
        $accessToken = json_decode($accessTokenRes, true)['access_token'];
        Redis::connection()->set(self::ACCESS_TOKEN_CACHE_KEY, $accessToken);
        Redis::connection()->expire(self::ACCESS_TOKEN_CACHE_KEY, 3600);
        return $accessToken;
    }

    protected function formatWxArticles($articles, $page, $pageSize)
    {
        $result = [
            'list' => [],
            'count' => 0,
            'page' => $page,
            'page_size' => $pageSize,
        ];

        $list = [];
        $newsItems = [];
        foreach ($articles['item'] as $key => $article) {
            foreach ($article['content']['news_item'] as $item) {
                $item['created_at'] = date('Y-m-d H:i:s', $article['content']['create_time']);
                array_push($newsItems, $item);
            }
        }

        foreach ($newsItems as $newsItem) {
            array_push($list, [
                'id' => $newsItem['title'],
                'title' => $newsItem['title'],
                'origin_url' => $newsItem['thumb_url'],
                'cover_url' => empty($newsItem['thumb_url']) ? '' : $this->saveWxPic($newsItem['thumb_url']),
                'detail_url' => $newsItem['url'],
                'created_at' => $newsItem['created_at'],
            ]);
        }

        $result['list'] = $list;
        $result['count'] = $this->fetchArticlesCount();
        return $result;
    }

    private function saveWxPic(string $url): string
    {
        $fileName = $this->getWxPicFileName($url);
        if (Storage::exists(self::SAVE_LOCAL_PIC_PATH . $fileName)) {
            return config('app.url') . Storage::url(self::SAVE_LOCAL_PIC_PATH . $fileName);
        }
        $client = new Client();
        $fileContent = $client->get($url)->getBody()->getContents();
        Storage::put(self::SAVE_LOCAL_PIC_PATH . $fileName, $fileContent);
        return config('app.url') . Storage::url(self::SAVE_LOCAL_PIC_PATH . $fileName);
    }

    private function getWxPicFileName(string $url): string
    {
        $arr = explode('wx_fmt=', $url);
        $ex = end($arr);
        return sprintf('%s.%s', md5($url), $ex);
    }

    private function flushCache()
    {
        Cache::flush();
        Redis::connection()->del(self::ACCESS_TOKEN_CACHE_KEY);
    }
}
